/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { Defects, IssueReport } from '../../model/Defects';
import { FileMatcher, MatcherCallback, MatcherTypes, StmtMatcher } from "../../matcher/Matchers";
import { Rule } from "../../model/Rule";
import { AbstractFieldRef, ArkAssignStmt, ArkDeleteExpr, ArkFile, ArkInstanceFieldRef, Stmt } from 'arkanalyzer';
import { CompositeMatcher } from '../../matcher/matcherAdapter/matchCommon';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'NoDynamicDeleteCheck');
const gmetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/no-dynamic-delete.md", // TODO: support url
    description: "Do not delete dynamically computed property keys."
};

export class NoDynamicDeleteCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gmetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
        match(node: ArkFile): boolean {
            return !node.getName().endsWith('.ets');
        }
    };
    private stmtMatcher: StmtMatcher = {
        matcherType: MatcherTypes.STMT,
        match(stmt: Stmt): boolean {
            return stmt instanceof ArkAssignStmt;
        }
    };

    public registerMatchers(): MatcherCallback[] {
        const matchBuildCb: MatcherCallback = {
            matcher: new CompositeMatcher([this.fileMatcher, this.stmtMatcher]),
            callback: this.check
        }
        return [matchBuildCb];
    }

    public check = (stmt: ArkAssignStmt) => {
        //检测动态访问 delete obj['XXX']
        const delValue = stmt.getUses().find(v => v instanceof ArkDeleteExpr);
        if (!delValue || !stmt.getOriginalText()?.includes('[')) {
            return;
        }
        const field = (delValue as ArkDeleteExpr).getField();
        if (field instanceof AbstractFieldRef) {
            //string index and can not replace with the constant equivalents, not report
            const isStr = field.getFieldName().indexOf('\'') > -1;
            const filedName = isStr ? field.getFieldName().replace(/'/g, '') : field.getFieldName();
            if (!isNaN(Number(filedName)) && filedName !== 'Infinity') {
                return;
            }
        }
        const position = stmt.getOperandOriginalPosition(field);
        if (!position) {
            return;
        }
        const offset = field instanceof ArkInstanceFieldRef ? field.getBase().getName().length : 0;
        const filePath = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile().getFilePath();
        const severity = this.rule.alert ?? this.metaData.severity;
        const defect = new Defects(position.getFirstLine(), position.getFirstCol() + offset, position.getLastCol(), this.metaData.description, severity,
            this.rule.ruleId, filePath, this.metaData.ruleDocPath, true, false, true);
        this.issues.push(new IssueReport(defect, undefined));
    }
}